package com.haoma.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.haoma.dto.ArticleDto;
import com.haoma.elasticsearch.ArticleEs;
import com.haoma.elasticsearch.ArticleMapper;
import com.haoma.entity.*;
import com.haoma.exception.BlogException;
import com.haoma.mapper.*;
import com.haoma.minio.MinIOConfig;
import com.haoma.minio.MinIOConfigProperties;
import com.haoma.service.ArticleService;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * Description: 文章相关业务
 */
@Service
public class ArticleServiceImpl implements ArticleService {

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private LabelMapper labelMapper;

    @Autowired
    private CategoryMapper categoryMapper;

    @Autowired
    private MinIOConfig minIOConfig;

    @Autowired
    private MinIOConfigProperties minIOConfigProperties;

    @Autowired
    private ArticleCategoryMapper articleCategoryMapper;

    @Autowired
    private ArticleLabelMapper articleLabelMapper;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;


    /**
     * 获取所有的分类和标签
     *
     * @return
     */
    public Map<String, List<String>> getLabelAndCategory() {
        // 存储返回数据
        Map<String, List<String>> stringListMap = new HashMap<>();
        List<Label> labels = labelMapper
                .selectList(new LambdaQueryWrapper<Label>().orderByDesc(Label::getCreateTime));
        List<String> labelList = labels.stream().map(Label::getLabelName).toList();
        stringListMap.put("label", labelList);
        List<Category> categories = categoryMapper
                .selectList(new LambdaQueryWrapper<Category>().orderByDesc(Category::getCreateTime));
        List<String> categoryList = categories.stream().map(Category::getName).toList();
        stringListMap.put("category", categoryList);
        return stringListMap;
    }

    /**
     * 上传文章封面
     *
     * @param multipartFile
     * @return
     */
    public String fileUpload(MultipartFile multipartFile) {
        return getUploadUrl("admin-article-cover", multipartFile);
    }

    /**
     * 发布文章
     *
     * @param articleEs
     */
    @Transactional
    public void saveArticle(ArticleEs articleEs) {
        // 当前时间戳
        long timestamp = System.currentTimeMillis();
        // 封装数据
        articleEs.setId(timestamp);
        articleEs.setHot(1);
        String format = DateUtil.format(new Date(), "yyyy-MM-dd");
        articleEs.setUpdateTime(format);
        articleEs.setCreateTime(format);
        // 保存到es中
        ArticleEs articleEsDb = articleMapper.save(articleEs);
        System.out.println(articleEsDb);

        // 查询标签表
        LambdaQueryWrapper<Label> labelLambdaQueryWrapper = new LambdaQueryWrapper<>();
        labelLambdaQueryWrapper.eq(Label::getLabelName, articleEs.getLabelName());
        Label label = labelMapper.selectOne(labelLambdaQueryWrapper);
        label.setLabelCount(label.getLabelCount() + 1);
        labelMapper.updateById(label);

        // 查询分类表
        LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<>();
        categoryLambdaQueryWrapper.eq(Category::getName, articleEs.getCategoryName());
        Category category = categoryMapper.selectOne(categoryLambdaQueryWrapper);
        category.setCount(category.getCount() + 1);
        categoryMapper.updateById(category);

        // 存储到关系表中
        ArticleCategoryRelation articleCategoryRelation = new ArticleCategoryRelation();
        articleCategoryRelation.setArticleId(articleEsDb.getId());
        articleCategoryRelation.setCategoryId(category.getId());
        articleCategoryMapper.insert(articleCategoryRelation);

        ArticleLabelRelation articleLabelRelation = new ArticleLabelRelation();
        articleLabelRelation.setLabelId(label.getId());
        articleLabelRelation.setArticleId(articleEsDb.getId());
        articleLabelMapper.insert(articleLabelRelation);
    }

    /**
     * 查询全部的es数据
     *
     * @return
     */
    public List<ArticleEs> getArticleList(ArticleDto articleDto) {
        // 初始化查询构建器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        // 查询全部数据
        nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
        // 根据排序字段排序
        nativeSearchQueryBuilder.withSorts(SortBuilders.fieldSort("sort"));

        // 组合多个查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 判断是否有
        if (StringUtils.hasText(articleDto.getTitle())) {
            // 标题模糊匹配
            boolQueryBuilder.must(QueryBuilders.fuzzyQuery("title", articleDto.getTitle()));
        }

        if (StringUtils.hasText(articleDto.getLabelName())) {
            // 标签精确匹配
            boolQueryBuilder.must(QueryBuilders.termQuery("labelName", articleDto.getLabelName()));
        }

        if (articleDto.getStatus() != null) {
            // 状态精确匹配
            boolQueryBuilder.must(QueryBuilders.termQuery("status", articleDto.getStatus()));
        }

        if (StringUtils.hasText(articleDto.getCategoryName())) {
            // 分类精确匹配
            boolQueryBuilder.must(QueryBuilders.termQuery("categoryName", articleDto.getCategoryName()));
        }

        if (!CollectionUtils.isEmpty(articleDto.getDateArray())) {
            // 转换为数组
            Object[] array = articleDto.getDateArray().toArray();
            // 时间范围查询
            boolQueryBuilder.must(QueryBuilders
                    // 需要访问查询的字段
                    .rangeQuery("createTime")
                    // 开始事件
                    .from(array[0])
                    // 结束事件
                    .to(array[1])
                    // 包含开始日期
                    .includeLower(true)
                    // 包含结束日期
                    .includeUpper(true)
            );
        }

        // 设置条件到主查询的构造器里
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

        // 构建查询对象
        NativeSearchQuery build = nativeSearchQueryBuilder.build();

        // 开始搜索
        SearchHits<ArticleEs> search = elasticsearchRestTemplate.search(build, ArticleEs.class);

        // 将结果处理
        List<ArticleEs> collect = search.get().map(SearchHit::getContent).toList();
        return collect;
    }

    /**
     * 修改文章
     *
     * @param id
     * @param articleEs
     */
    public void updateArticle(Long id, ArticleEs articleEs) {
        // 查询es获取到装满数据的容器类
        Optional<ArticleEs> optionalArticleEs = articleMapper.findById(id);
        if (optionalArticleEs.isEmpty()) {
            throw new BlogException("修改失败！");
        }
        // 获取到内容
        ArticleEs articleEsDb = optionalArticleEs.get();
        // 修改
        BeanUtils.copyProperties(articleEs, articleEsDb);
        articleEsDb.setUpdateTime(getCurrentDate());
        articleMapper.save(articleEsDb);
    }

    /**
     * 修改文章状态
     *
     * @param id
     * @param status
     */
    public void updateStatus(Long id, Integer status) {
        // 查询文章
        Optional<ArticleEs> optionalArticleEs = articleMapper.findById(id);
        if (optionalArticleEs.isEmpty()) {
            throw new BlogException(status == 1 ? "显示失败!" : "隐藏失败!");
        }
        // 获取到文章
        ArticleEs articleEs = optionalArticleEs.get();
        articleEs.setUpdateTime(getCurrentDate());
        articleEs.setStatus(status);
        articleMapper.save(articleEs);
    }

    @Autowired
    private ArticleDbMapper articleDbMapper;

    /**
     * 删除文章
     *
     * @param id
     */
    public void delArticle(Long id) {
        // 查询文章
        Optional<ArticleEs> optionalArticleEs = articleMapper.findById(id);
        if (optionalArticleEs.isEmpty()) {
            throw new BlogException("删除失败!");
        }
        ArticleEs articleEs = optionalArticleEs.get();

        // 将删除的文章留存
        Article article = new Article();
        BeanUtils.copyProperties(articleEs, article);
        article.setArticleDescribe(articleEs.getDescribe());
        article.setArticleId(articleEs.getId());
        articleDbMapper.insert(article);

        // 根据id删除该文章的数据并且吧文章分类表和文章标签表的数据删除
        articleCategoryMapper.delete(new LambdaQueryWrapper<ArticleCategoryRelation>().eq(ArticleCategoryRelation::getArticleId, articleEs.getId()));
        articleLabelMapper.delete(new LambdaQueryWrapper<ArticleLabelRelation>().eq(ArticleLabelRelation::getArticleId, articleEs.getId()));

        // 删除文章
        articleMapper.delete(articleEs);
    }

    /**
     * 根据id获取到文章
     *
     * @param id
     * @return
     */
    public ArticleEs getArticle(Long id) {
        Optional<ArticleEs> optionalArticleEs = articleMapper.findById(id);
        if (optionalArticleEs.isEmpty()) {
            throw new BlogException("修改失败!");
        }
        return optionalArticleEs.get();
    }

    @Override
    public String uploadImage(MultipartFile multipartFile) {
        return getUploadUrl("admin-article-image", multipartFile);
    }

    private String getUploadUrl(String path, MultipartFile multipartFile) {
        try {
            // 连接客户端
            MinioClient minioClient = minIOConfig.buildMinioClient();
            // 判断桶是否存在
            boolean isBucket = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minIOConfigProperties.getBucket()).build());
            if (!isBucket) {
                // 不存在创建一个
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minIOConfigProperties.getBucket()).build());
            } else {
                // 提示程序员存在
                System.out.println("Bucket 'test' already exists.");
            }

            // 设置存储路径
            // 当天作为目录
            String date = DateUtil.format(new Date(), "yyyyMMdd");
            // 唯一id作为图片路径
            String uuid = UUID.randomUUID().toString().replace("-", "");
            // 组合＋业务
            String fileName = path + "/" + date + "/" + uuid + multipartFile.getOriginalFilename();
            System.out.println(fileName);

            // 构建上传参数
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(minIOConfigProperties.getBucket())
                    .stream(multipartFile.getInputStream(), multipartFile.getSize(), -1)
                    .object(fileName)
                    .build();
            // 上传
            minioClient.putObject(putObjectArgs);
            // 将路径返回
            return minIOConfigProperties.getReadPath() + "/" + minIOConfigProperties.getBucket() + "/" + fileName;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取到当前时间
     *
     * @return
     */
    public String getCurrentDate() {
        return DateUtil.format(new Date(), "yyyy-MM-dd");
    }
}

